#pragma once
#include <Common/platform.h>
#include <Common/device_type.h>

namespace shermit {
namespace lbvh {
	template<class BVSOA_t>
	class LBvhLeaves {
	public:
		int* parent;
		int* primIdx;
		int* segLen, * segOffset;
		//int* escape;
		//unsigned int *morton;
		unsigned int* morton;
		BVSOA_t bv;

		void allocateHost(int size) {
			parent = new int[size];
			primIdx = new int[size];
			segLen = new int[size];
			memset(segLen, 0, sizeof(int) * size);
			segOffset = new int[size];
			morton = new unsigned int[size];
			bv.allocateHost(size);
		}
		void deallocateHost() {
			delete[] parent;
			delete[] primIdx;
			delete[] segLen;
			delete[] segOffset;
			delete[] morton;
			bv.deallocateHost();
		}
#if enable_cuda
		void allocateDevice(int size) {
			checkCudaErrors(GPUMalloc(parent, sizeof(int) * size));
			checkCudaErrors(GPUMalloc(primIdx, sizeof(int) * size));
			checkCudaErrors(GPUMalloc(segLen, sizeof(int) * size));
			checkCudaErrors(cudaMemset(segLen, 0, sizeof(int) * size));
			checkCudaErrors(GPUMalloc(segOffset, sizeof(int) * size));
			//checkCudaErrors(GPUMalloc(escape, sizeof(int) * size));
			checkCudaErrors(GPUMalloc(morton, sizeof(unsigned int) * size));
			bv.allocateDevice(size);
		}
		void deallocateDevice() {
			checkCudaErrors(cudaFree(parent));
			checkCudaErrors(cudaFree(primIdx));
			checkCudaErrors(cudaFree(segLen));
			checkCudaErrors(cudaFree(segOffset));
			//checkCudaErrors(cudaFree(escape));
			checkCudaErrors(cudaFree(morton));
			bv.deallocateDevice();
		}
#endif
	};

	template<class BVSOA_t>
	class LBvhInteriors {
	public:
		int* parent;
		int* lc, * rc;
		int* rangeL, * rangeR;
		//int* escape;
		BVSOA_t bv;

		void allocateHost(int size) {
			parent = new int[size];
			lc = new int[size];
			rc = new int[size];
			rangeL = new int[size];
			rangeR = new int[size];
			bv.allocateHost(size);
		}
		void deallocateHost() {
			delete[] parent;
			delete[] lc;
			delete[] rc;
			delete[] rangeL;
			delete[] rangeR;
			bv.deallocateHost();
		}
#if enable_cuda
		void allocateDevice(int size) {
			checkCudaErrors(GPUMalloc(parent, sizeof(int) * size));
			checkCudaErrors(GPUMalloc(lc, sizeof(int) * size));
			checkCudaErrors(GPUMalloc(rc, sizeof(int) * size));
			checkCudaErrors(GPUMalloc(rangeL, sizeof(int) * size));
			checkCudaErrors(GPUMalloc(rangeR, sizeof(int) * size));
			bv.allocateDevice(size);
		}
		void deallocateDevice() {
			checkCudaErrors(cudaFree(parent));
			checkCudaErrors(cudaFree(lc));
			checkCudaErrors(cudaFree(rc));
			checkCudaErrors(cudaFree(rangeL));
			checkCudaErrors(cudaFree(rangeR));
			bv.deallocateDevice();
		}
#endif
	};

	struct InteriorBuildAid {
		unsigned int* metrics;
		int* visitCount;
		int* leftLeafCount; // height to the leftmost leaf
	};

	inline GENERAL int isLeaf(const int idx, int* idx_original) {
		if (idx < 0) {
			*idx_original = -idx - 1;
			return 0;
		}
		*idx_original = idx;
		return 1;
	}

	inline GENERAL int interiorIdx(const int idx) {
		return -idx - 1;
	}

}
}